/* 
 * ==================== vector add or sub ====================
 */

module add_sub_vector(/*AUTOARG*/
   // Outputs
   o_sum_diff, o_carrys_borrows,
   // Inputs
   i_a, i_b, i_mode, i_add_sub_ctrl
   );

   parameter width = 64;		// total width
   parameter granu = 8;			// granularity(8/16/32/64)
   localparam fields_num = width / granu;
   localparam mode_num = log2(fields_num) + 1; // the num of mode

   output logic [width-1:0] o_sum_diff;
   // output logic [fields_num-1:0] o_carrys_borrows_n; // carrys for add or inv of borrows for sub
   output logic [fields_num-1:0] o_carrys_borrows;
   input logic [width-1:0] 		 i_a, i_b;
   input logic [mode_num-1:0] 			   i_mode;
   input logic [fields_num-1:0] 		   i_add_sub_ctrl;	// 0:add 1:sub
   
   // ==================== inv mask for sub operation ====================
   logic [fields_num-1:0][granu-1:0] 	   op_a_inv_mask;

   always_comb
	 begin
		logic inv_flag;
		for(int i=0;i<fields_num;i++)
		  begin
			 inv_flag = 1'b0;
			 for(int j=0;j<mode_num;j++)
			   inv_flag |= i_mode[j] & i_add_sub_ctrl[(i/(2**j)+1)*(2**j)-1];

			 op_a_inv_mask[i] = inv_flag ? '1 : '0;
		  end
	 end
   
   // ==================== add operation ====================
   logic [width-1:0] 	   sum;
   logic [fields_num-1:0]    carrys;
   
   add_vector #(.width(width),
				.granu(granu)) U_add_vector(
											// Outputs
											.o_sum				(sum),
											.o_carrys			(carrys),
											// Inputs
											.i_a				(i_a ^ op_a_inv_mask),
											.i_b				(i_b),
											.i_mode				(i_mode) );
   
   
   // a+b or a-b (~(~a+b))
   assign o_sum_diff = sum ^ op_a_inv_mask;
   assign o_carrys_borrows = carrys; // if add select, this is the carrys; otherwise the borrows
   // assign o_carrys_borrows_n = carrys ^ i_add_sub_ctrl;
   
endmodule // add_sub_vector
